package com.eastdigit.servlet;

import com.eastdigit.system.model.BusinessException;
import com.eastdigit.system.model.IDepartment;
import com.eastdigit.system.service.IDepartmentService;
import com.eastdigit.system.service.IPermissionService;
import com.eastdigit.util.LogUtil;
import com.eastdigit.util.StringUtil;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.logging.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by hehz on 2016/7/15.
 */
@Aspect
@Component
public class DeptPermAspect {

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private IDepartmentService departmentService;

    private Log log = LogUtil.getLogger();

    /**
     * 权限校验
     * 
     * @param joinPoint
     */
    @Before("@annotation(com.eastdigit.servlet.DeptPermValidate)")
    public void before(JoinPoint joinPoint) {
        DeptPermValidate deptPermValidate = (DeptPermValidate) getMethodAnnotation(joinPoint, DeptPermValidate.class);
        HttpSession session = request.getSession();
        Map<String, String> map = (Map<String, String>) session.getAttribute("_current_menu_cache");
        ReqBean reqBean = (ReqBean) request.getAttribute(ReqBean.NAME);

        String menuId = reqBean.getMenuId();
        if (StringUtil.isEmpty(menuId)) {
            throw new BusinessException("未知异常，请刷新页面重新操作！");
        }

        log.trace("validate：menuId：" + menuId + "：" + reqBean.getUrl());

        for (int i = 0; i < deptPermValidate.id().length; i++) {// 存在多个校验
            IdConfig config = new IdConfig(deptPermValidate.id()[i]);
            String[] idValues = new String[config.ids.length];

            boolean isAdd = false;// 是否是新增
            for (int j = 0; j < config.ids.length; j++) {
                String id = config.ids[j], idMapping = "{" + id + "}";
                idValues[j] = reqBean.getInfo(id);// 先从请求参数中获取值

                if (StringUtil.isEmpty(idValues[j])) {// 没有获取到值
                    // 判断是否是通过url resetfull方式传递过来的
                    RequestMapping mapping = (RequestMapping) getMethodAnnotation(joinPoint, RequestMapping.class);
                    String mappingValue = mapping.value()[0].replaceAll(" ", "");

                    if (mappingValue.contains(idMapping)) {
                        String url = reqBean.getUrl();
                        if (url.contains(".")) {// 去掉.后面的部分
                            url = url.substring(0, url.indexOf("."));
                        }
                        // controller层级
                        mapping = joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class);
                        if (mapping.value().length > 0) {
                            url = url.substring(mapping.value()[0].length());
                        }
                        String[] arr1 = url.split("/"), arr2 = mappingValue.split("/");
                        for (int k = 0; k < arr2.length; k++) {
                            if (arr2[k].equals(idMapping)) {
                                idValues[j] = arr1[k];
                                break;
                            }
                        }
                    } else {
                        isAdd = true;
                    }
                }
            }

            if (!isAdd && !menuId.equals(session.getAttribute("_current_menu_id"))) {// 除新增外必须先查询
                throw new BusinessException("未知异常，请刷新页面重新操作！");
            }
            // 新增的时候从参数中获取部门ID
            String deptId = isAdd ? reqBean.getInfo(deptPermValidate.deptId()[Math.min(i,
                    deptPermValidate.deptId().length - 1)]) : map.get(config.prefix + "|" + StringUtil.join(idValues));

            log.trace("validate：id：" + config.prefix + "|" + StringUtil.join(idValues) + "：deptId：" + deptId);
            if (StringUtil.isEmpty(deptId)) {
                throw new BusinessException("您没有操作该数据的权限！");
            }
            if (!permissionService.validateUrlPermissionsOfUser(reqBean.getUserId(), menuId, reqBean.getUrl(), deptId)) {
                throw new BusinessException("您没有操作该数据的权限！");
            }
        }
    }

    /**
     * 部门操作权限校验
     * 
     * @param joinPoint
     */
    @Before("@annotation(com.eastdigit.servlet.DeptOptPermValidate)")
    public void beforeDeptOpt(JoinPoint joinPoint) {
        DeptOptPermValidate annotation = (DeptOptPermValidate) getMethodAnnotation(joinPoint, DeptOptPermValidate.class);
        HttpSession session = request.getSession();
        Map<String, String> map = (Map<String, String>) session.getAttribute("_current_menu_cache");
        ReqBean reqBean = (ReqBean) request.getAttribute(ReqBean.NAME);

        String menuId = reqBean.getMenuId();
        if (StringUtil.isEmpty(menuId)) {
            throw new BusinessException("未知异常，请刷新页面重新操作！");
        }

        log.trace("validate：menuId：" + menuId + "：" + reqBean.getUrl());

        String deptId = reqBean.getInfo(annotation.id());

        boolean isAdd = StringUtil.isEmpty(deptId);// 是否是新增

        if (!isAdd && !menuId.equals(session.getAttribute("_current_menu_id"))) {// 除新增外必须先查询
            throw new BusinessException("未知异常，请刷新页面重新操作！");
        }
        // 新增的时候从参数中获取部门ID
        String validateDeptId = isAdd ? reqBean.getInfo(annotation.deptId()) : map.get(annotation.id() + "|" + deptId);

        if (StringUtil.isEmpty(validateDeptId)) {
            throw new BusinessException("您没有操作该部门的权限！");
        }
        if (!permissionService.validateUrlPermissionsOfUser(reqBean.getUserId(), menuId, reqBean.getUrl(),
                validateDeptId)) {
            throw new BusinessException("您没有操作该部门的权限！");
        }
        if (annotation.isUpdate()) {// 修改
            String parentDeptId = reqBean.getInfo(annotation.parentDeptId());
            IDepartment department = departmentService.queryDepartment(deptId);
            if (department == null) {
                throw new BusinessException("查找部门失败！");
            }
            // 部门没变不校验
            if (!department.getParentDeptId().equals(parentDeptId)) {
                if (!permissionService.validateUrlPermissionsOfUser(reqBean.getUserId(), reqBean.getMenuId(),
                        reqBean.getUrl(), parentDeptId)) {
                    throw new BusinessException("您没有操作该部门的权限！");
                }
            }
        }
    }

    /**
     * 权限准备
     * 
     * @param joinPoint
     * @param returnVal
     */
    @AfterReturning(pointcut = "@annotation(com.eastdigit.servlet.DeptPermPrepare)", returning = "returnVal")
    public void afterReturning(JoinPoint joinPoint, Object returnVal) {
        DeptPermPrepare deptPermPrepare = (DeptPermPrepare) getMethodAnnotation(joinPoint, DeptPermPrepare.class);
        ReqBean reqBean = (ReqBean) request.getAttribute(ReqBean.NAME);

        if (StringUtil.isNotEmpty(reqBean.getMenuId()) && returnVal instanceof JSONObject) {
            Object data = ((JSONObject) returnVal).opt("data");

            HttpSession session = request.getSession();
            Map<String, String> map = (Map<String, String>) session.getAttribute("_current_menu_cache");
            if (!reqBean.getMenuId().equals(session.getAttribute("_current_menu_id"))) {// 新的页面
                map = new HashMap<String, String>();
                session.setAttribute("_current_menu_id", reqBean.getMenuId());
                session.setAttribute("_current_menu_cache", map);
            }

            if (data instanceof JSONObject) {
                JSONArray rows = ((JSONObject) data).optJSONArray("rows");
                if (rows != null) {// 表格的数据
                    for (int i = 0; i < rows.size(); i++) {
                        JSONObject json = rows.getJSONObject(i);
                        prepareDataScope(deptPermPrepare, json, map);
                    }
                } else {
                    prepareDataScope(deptPermPrepare, (JSONObject) data, map);
                }
            } else if (data instanceof JSONArray) {
                JSONArray rows = ((JSONArray) data);
                for (int i = 0; i < rows.size(); i++) {
                    JSONObject json = rows.getJSONObject(i);
                    prepareDataScope(deptPermPrepare, json, map);
                    // 递归子节点
                    prepareChildrenDataScope(deptPermPrepare, json.optJSONArray("children"), map);
                }
            }
            for (String id : map.keySet()) {
                log.trace("prepare：" + id + "：" + map.get(id));
            }
        }
    }

    /**
     * 获取方法Annotation
     * 
     * @param joinPoint
     * @param clazz
     * @return
     */
    private Annotation getMethodAnnotation(JoinPoint joinPoint, Class clazz) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Class[] argClasses = null;
        if (args != null) {
            argClasses = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                argClasses[i] = args[i].getClass();
            }
        }
        try {
            return joinPoint.getTarget().getClass().getMethod(methodName, argClasses).getAnnotation(clazz);
        } catch (NoSuchMethodException e) {

        }
        return null;
    }

    /**
     * 缓存id和部门对应关系
     * 
     * @param deptPermPrepare
     * @param json
     * @param map
     */
    private void prepareDataScope(DeptPermPrepare deptPermPrepare, JSONObject json, Map<String, String> map) {
        IdConfig config = new IdConfig(deptPermPrepare.id());
        String[] idValues = new String[config.ids.length];

        for (int j = 0; j < config.ids.length; j++) {
            String idValue = json.optString(config.ids[j]);
            if (idValue == null) {
                throw new BusinessException(request.getServletPath() + "返回id名称设置错误！");
            }
            idValues[j] = idValue;
        }
        String deptId = json.optString(deptPermPrepare.deptId());
        if (deptId == null) {
            throw new BusinessException(request.getServletPath() + "返回deptId名称设置错误！");
        }
        map.put(config.prefix + "|" + StringUtil.join(idValues), deptId);
    }

    private void prepareChildrenDataScope(DeptPermPrepare deptPermPrepare, JSONArray array, Map<String, String> map) {
        if (array != null) {
            for (int i = 0; i < array.size(); i++) {
                JSONObject json = array.getJSONObject(i);
                prepareDataScope(deptPermPrepare, json, map);

                prepareChildrenDataScope(deptPermPrepare, json.optJSONArray("children"), map);
            }
        }
    }

    class IdConfig {
        String prefix;
        String ids[];

        public IdConfig(String id) {
            id = id.replaceAll(" ", "");
            prefix = id;
            ids = id.split(",");
        }
    }
}
